package org.cagt.standard;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * Class counting card collection statistics.
 * 
 * @author konrad.szalkowski (at) gmail.com
 * 
 */
public class CardCollectionStats {

	/*
	 * Computation subjects and tools.
	 */
	private Collection<StandardCard> cardCollection;
	private int maximalNumberOfCardOccurences;
	private int lastCollectionHash;

	public CardCollectionStats(Collection<StandardCard> cardCollection,
			int maximalNumberOfCardOccurences) {
		// Start terms
		if (cardCollection == null) {
			throw new NullPointerException();
		}
		if (maximalNumberOfCardOccurences <= 0) {
			throw new IllegalArgumentException(
					"maximalNumberOfCardOccurence must be above 0");
		}
		this.cardCollection = cardCollection;
		this.maximalNumberOfCardOccurences = maximalNumberOfCardOccurences;
	}

	public CardCollectionStats(Set<StandardCard> cardCollection) {
		this(cardCollection, 1);
	}

	/*
	 * Computation results
	 */
	private Map<Figure, Integer> figureCount;
	private Map<Color, Integer> colorCount;
	private List<Integer> figureCooccurrences;
	private List<Integer> colorCooccurrences;

	public void count() {
		final Figure[] figures = Figure.values();
		final Color[] colors = Color.values();
		// Count
		int[] figureCount = new int[figures.length];
		int[] colorCount = new int[colors.length];
		this.figureCount = new HashMap<Figure, Integer>();
		this.colorCount = new HashMap<Color, Integer>();
		for (StandardCard card : cardCollection) {
			figureCount[card.getFigure().ordinal()]++;
			colorCount[card.getColor().ordinal()]++;
		}
		for (Figure figure : figures) {
			this.figureCount.put(figure, figureCount[figure.ordinal()]);
		}
		for (Color color : colors) {
			this.colorCount.put(color, colorCount[color.ordinal()]);
		}
		// Count the counts :)
		int[] figureCooccurences = new int[colors.length
				* maximalNumberOfCardOccurences + 1];
		int[] colorCooccurrences = new int[figures.length
				* maximalNumberOfCardOccurences + 1];
		this.figureCooccurrences = new ArrayList<Integer>();
		this.colorCooccurrences = new ArrayList<Integer>();
		for (int i = 0; i < figureCount.length; i++) {
			figureCooccurences[figureCount[i]]++;
		}
		for (int i = 0; i < colorCount.length; i++) {
			colorCooccurrences[colorCount[i]]++;
		}
		for (int i = 0; i < figureCooccurences.length; i++) {
			this.figureCooccurrences.add(figureCooccurences[i]);
		}
		for (int i = 0; i < colorCooccurrences.length; i++) {
			this.colorCooccurrences.add(colorCooccurrences[i]);
		}
	}

	/**
	 * Map of how many cards with figures of given kind there are in given
	 * collection.
	 */
	public Map<Figure, Integer> getFigureCount() {
		int currentHash = cardCollection.hashCode();
		if (lastCollectionHash != currentHash) {
			count();
			this.figureCount = Collections.unmodifiableMap(this.figureCount);
			lastCollectionHash = currentHash;
		}
		return figureCount;
	}

	/**
	 * Map of how many cards with colors of given kind there are in given
	 * collection.
	 */
	public Map<Color, Integer> getColorCount() {
		int currentHash = cardCollection.hashCode();
		if (lastCollectionHash != currentHash) {
			count();
			this.colorCount = Collections.unmodifiableMap(this.colorCount);
			lastCollectionHash = currentHash;
		}
		return colorCount;
	}

	/**
	 * Map of how many duplications in given collection.
	 */
	public List<Integer> getFigureCooccurences() {
		int currentHash = cardCollection.hashCode();
		if (lastCollectionHash != currentHash) {
			count();
			this.figureCooccurrences = Collections
					.unmodifiableList(figureCooccurrences);
			lastCollectionHash = currentHash;
		}
		return figureCooccurrences;
	}

	/**
	 * Map of color duplications in given collection.
	 */
	public List<Integer> getColorCooccurences() {
		int currentHash = cardCollection.hashCode();
		if (lastCollectionHash != currentHash) {
			count();
			this.colorCooccurrences = Collections
					.unmodifiableList(colorCooccurrences);
			lastCollectionHash = currentHash;
		}
		return colorCooccurrences;
	}
}
